#include "task_led_ctrl.h"
// std-lib
#include <string.h>
// app
#include "str_gbk_2312.h"
// lib
#include "debug.h"

// ---------------------------------------------------------------------------------
// ---------------------------------------------------------------------------------
// ---------------------------------------------------------------------------------
// sys
#include <unistd.h>
// #include "FreeRTOS.h"
// #include "queue.h"

#define STATE_MACHINE_DELAY_MS(ms)          usleep(ms)
#define LED_CRTL_HEAP_MALLOC(ulSize)        malloc(ulSize)
#define LED_CRTL_HEAP_FREE(p)               free(p)

// static QueueHandle_t tLedCtrlAsynQueue = NULL;                                   // LED控制任务队列定义

typedef struct {                                                                    // LED控制标志位异步处理任务队列参数
    uint8_t byLedCtrlFlag;                                                          // LED控制标志位
} asyn_led_ctrl_queue_parm_t;

// #define TASK_HSA_LEN                         5                                      // 队列长度
// #define TASK_HSA_SIZE                        sizeof(asyn_led_ctrl_queue_parm_t)     // 队列大小

static void led_scr_set_bottom_parm(uint16_t hwHor, uint16_t hwVer, uint8_t byFontSize, uint8_t byPixelPitch) {
    led_init_parm_zhuoyue(hwHor, hwVer, byFontSize, byPixelPitch);
    led_set_screen_config_zhuoyue();
    led_set_area_config_zhuoyue();
};

static void led_set_area_display_content(uint16_t hwAreaNum, uint8_t *pData, uint16_t hwDataLen) {
    led_set_area_display_content_zhuoyue(hwAreaNum, pData, hwDataLen);
};
// ---------------------------------------------------------------------------------
// ---------------------------------------------------------------------------------
// ---------------------------------------------------------------------------------

typedef struct {                            // 
    uint8_t byLedCtrlFlag;                  // LED控制标志位
    void (*fnLedCtrlFunc)(uint8_t byFlag);  // LED控制  byFlag:是否与上次显示控制位一致
} led_ctrl_t;

typedef struct {
    // 基础配置
    uint16_t        hwHor;                  // 水平分辨率
    uint16_t        hwVer;                  // 垂直分辨率
    uint8_t         byFontSize;             // 字体大小

    fn_pc_v         fnGetDevIdStr;          // 获取设备ID字符串
    fn_pc_v         fnGetDevNameStr;        // 获取设备名称字符串

    fn_u32_v        fnGetNodeSum;           // 获取指标总数
    fn_u32_u32      fnGetMntrSum;           // 获取检测项总数

    fn_v_pc         fnGetDate;              // 获取日期字符串，形如 "2023-04-10"
    fn_v_pc         fnGetTime;              // 获取时间字符串，形如 "12:34"
    fn_u8_v         fnGetWeekday;           // 获取星期几，0 表示星期一

    fn_pc_u32       fnGetNodeName;          // 获取节点名称字符串
    fn_pc_u32u32    fnGetMntrName;          // 获取检测项名称字符串
    fn_pc_u32u32    fnGetMntrValue;         // 获取检测项值字符串
    fn_pc_u32u32    fnGetMntrUnit;          // 获取检测项单位字符串

    // 推导参数
    uint8_t         byRowSum;               // 每页行数
    uint8_t         byNode1Row;             // 每个节点第一页能显示的行数
    uint8_t         byNode2_Row;            // 每个节点第一页以后能显示的行数
    uint32_t        uDataTotalPages;        // 总页数（显示页数时）

    // 运行参数
    uint8_t         byCtrlFlag;             // 控制标志位
    uint32_t        uPerPageTime;           // 每页显示时间（秒）
} led_scr_parm_t;

static led_scr_parm_t s_tLedScrParm = {
    // .hwHor = LED_RESOLUTION_HOR,
    // .hwVer = LED_RESOLUTION_VER,
    // .byFontSize = LED_FONT_SIZE,
};

static void led_ctrl_display_init_interface(uint8_t byFlag);
static void led_ctrl_display_get_cfg(uint8_t byFlag);
static void led_ctrl_display_show_data(uint8_t byFlag);
static void led_ctrl_display_err_print(uint8_t byFlag);

static const led_ctrl_t tLedCtrlTable[] = {
    {LCF_init_interface,    led_ctrl_display_init_interface},
    {LCF_get_cfg,           led_ctrl_display_get_cfg},
    {LCF_show_data,         led_ctrl_display_show_data},
    {LCF_err_print,         led_ctrl_display_err_print},
};

/// @brief 更新LED屏幕参数
static void refresh_led_scr_parm(void) {
    // 更新简单参数
    s_tLedScrParm.byRowSum = s_tLedScrParm.hwVer / s_tLedScrParm.byFontSize;
    s_tLedScrParm.byNode1Row = s_tLedScrParm.byRowSum - 1;
    s_tLedScrParm.byNode2_Row = s_tLedScrParm.byRowSum - 2;
    // 更新 uDataTotalPages 参数
    uint32_t uCntPage = 1;                                                          // 第一页总是显示时间
    for (uint32_t uNodeI = 0; uNodeI < s_tLedScrParm.fnGetNodeSum(); uNodeI++) {
        uint32_t uMntrSum = s_tLedScrParm.fnGetMntrSum(uNodeI);
        uCntPage += uMntrSum > s_tLedScrParm.byNode2_Row ? 
            // 如果一页不能显示完，则计算需要的页数（向上取整除法）
            1 + (uMntrSum - s_tLedScrParm.byNode2_Row + s_tLedScrParm.byNode1Row - 1) / s_tLedScrParm.byNode1Row : 
            // 如果一页能显示完，则直接加1
            1;
    }
    s_tLedScrParm.uDataTotalPages = uCntPage;
    _DEBUG_PRINTF(DBG_STP, "Total pages: %d, row sum: %d\n", s_tLedScrParm.uDataTotalPages, s_tLedScrParm.byRowSum);
}

/// @brief 根据LED页码获取节点和对应显示第一项监控指标的序号
/// @param uPages LED页码(不能为0)
/// @param puNodeNum 节点序号
/// @param puMonitorNum 监测项序号
/// @return true 成功
static bool get_node_and_monitor_by_led_page(uint32_t uPages, uint32_t *puNodeNum, uint32_t *puMonitorNum) {
    if (uPages == 0 || uPages >= s_tLedScrParm.uDataTotalPages || !puNodeNum || !puMonitorNum) {
        return false;
    }

    for (uint32_t uNodeI = 0, uCntPage = 1; uNodeI < s_tLedScrParm.fnGetNodeSum(); uNodeI++) {
        uint32_t uMntrSum = s_tLedScrParm.fnGetMntrSum(uNodeI);
        // 计算当前节点所需页数
        uint32_t uCurNodePage = uMntrSum > s_tLedScrParm.byNode2_Row ? 
            // 如果一页不能显示完，则计算需要的页数（向上取整除法）
            1 + (uMntrSum - s_tLedScrParm.byNode2_Row + s_tLedScrParm.byNode1Row - 1) / s_tLedScrParm.byNode1Row : 
            // 如果一页能显示完，则直接加1
            1;
        // 如果目标页落在当前节点
        if ((uPages >= uCntPage) && (uPages < uCntPage + uCurNodePage)) {
            *puNodeNum = uNodeI;
            *puMonitorNum = (uPages == uCntPage) ? 0 :  // 第一页
                (uPages - uCntPage - 1) * s_tLedScrParm.byNode1Row + s_tLedScrParm.byNode2_Row;
            return true;
        }
        uCntPage += uCurNodePage;
    }
    return false;
}

/// @brief 显示空页面
/// @param byLine 行数（包括此行）
static void led_ctrl_display_empty(uint8_t byLine) {
    for (; byLine < s_tLedScrParm.byRowSum; byLine++) {
        led_set_area_display_content(byLine, (uint8_t *)EMPTY_GBK_STR, strlen(EMPTY_GBK_STR));
    }
}

/// @brief 参数检查
static void led_ctrl_check_parm(void) {
    if (s_tLedScrParm.byRowSum >= 3) {       // 至少3行显示
        return;
    }
    if (s_tLedScrParm.fnGetNodeSum != NULL) {
        return;
    }
    if (s_tLedScrParm.fnGetDate != NULL) {
        return;
    }
    while (1) {
        _DEBUG_PRINT(DBG_STP, "err!\n");
        STATE_MACHINE_DELAY_MS(100);
    }
}

/// @brief 显示联网中
static void led_ctrl_display_init_interface(uint8_t byFlag) {
    // 设置第1行显示内容 "设备ID：123456789012"
    char sz1LineText[32] = {0};
    sprintf(sz1LineText, "%s%s", DEV_ID_TIPS_GBK_STR, s_tLedScrParm.fnGetDevIdStr());
    led_set_area_display_content(0, (uint8_t *)sz1LineText, strlen(sz1LineText));
    // 设置第2行显示内容 "联网中..."
    led_set_area_display_content(1, (uint8_t *)NETWORKING_GBK_STR, strlen(NETWORKING_GBK_STR));
    // 后续不显示内容
    led_ctrl_display_empty(2);
}

/// @brief 显示配置中
static void led_ctrl_display_get_cfg(uint8_t byFlag) {
    // 设置第1行显示内容 LED名
    led_set_area_display_content(0, (uint8_t *)s_tLedScrParm.fnGetDevNameStr(), strlen(s_tLedScrParm.fnGetDevNameStr()));
    // 设置第2行显示内容 "配置中..."
    led_set_area_display_content(1, (uint8_t *)NETWORKING_GBK_STR, strlen(NETWORKING_GBK_STR));
    // 后续不显示内容
    led_ctrl_display_empty(2);
}

/// @brief 显示具体数据
static void led_ctrl_display_show_data(uint8_t byFlag) {
    static uint32_t uPageCnt = 0;           // 用于计数的变量
    char szLineText[64] = {0};
    // 设置第1行显示内容 LED名
    led_set_area_display_content(0, (uint8_t *)s_tLedScrParm.fnGetDevNameStr(), strlen(s_tLedScrParm.fnGetDevNameStr()));
    if (uPageCnt == 0) {
        // 设置第2行显示内容 "20xx-xx-xx"
        memset(szLineText, 0, sizeof(szLineText));
        s_tLedScrParm.fnGetDate(szLineText);
        led_set_area_display_content(0, (uint8_t *)szLineText, strlen(szLineText));
        // 设置第3行显示内容 "xx:xx"
        memset(szLineText, 0, sizeof(szLineText));
        s_tLedScrParm.fnGetTime(szLineText);
        led_set_area_display_content(0, (uint8_t *)szLineText, strlen(szLineText));
        // 设置第4行显示内容 "星期？"
        memset(szLineText, 0, sizeof(szLineText));
        led_set_area_display_content(0, (uint8_t *)get_weekday_gbk_str(s_tLedScrParm.fnGetWeekday()), strlen(get_weekday_gbk_str(s_tLedScrParm.fnGetWeekday())));
        // 后续不显示内容
        led_ctrl_display_empty(4);
    } else {
        uint32_t uNodeNum = 0;
        uint32_t uMonitorNum = 0;
        if (get_node_and_monitor_by_led_page(uPageCnt, &uNodeNum, &uMonitorNum)) {
            _DEBUG_PRINTF(DBG_STP, "Page %d, NodeNum %d, MonitorNum %d\n", uPageCnt, uNodeNum, uMonitorNum);
            for (uint32_t i = 1; i < s_tLedScrParm.byNode1Row || uMonitorNum < s_tLedScrParm.fnGetMntrSum(uNodeNum); i++, uMonitorNum++) {
                if (uNodeNum == 0) {
                    // 如果是第一个节点，先显示节点名
                    memset(szLineText, 0, sizeof(szLineText));
                    sprintf(szLineText, "%s", s_tLedScrParm.fnGetNodeName(uNodeNum));
                    led_set_area_display_content(i, (uint8_t *)szLineText, strlen(szLineText));
                    continue;
                }
                // 获取第i行显示内容 "土壤PH: 5.6"
                memset(szLineText, 0, sizeof(szLineText));
                sprintf(szLineText, "%s: %s%s", 
                    s_tLedScrParm.fnGetMntrName(uNodeNum, uMonitorNum), 
                    s_tLedScrParm.fnGetMntrValue(uNodeNum, uMonitorNum), 
                    s_tLedScrParm.fnGetMntrUnit(uNodeNum, uMonitorNum)
                );
                led_set_area_display_content(i, (uint8_t *)szLineText, strlen(szLineText));
            }
        } else {
            _DEBUG_PRINT(DBG_STP, "err\n");
        }
    }
    // 更新显示页码
    uPageCnt = ++uPageCnt >= s_tLedScrParm.uDataTotalPages ? 0 : uPageCnt;
    // 根据标志位重置页码
    uPageCnt = byFlag ? uPageCnt : 0;
}

/// @brief 显示错误码
static void led_ctrl_display_err_print(uint8_t byFlag) {
    // 设置第1行显示内容 "设备ID：123456789012"
    char sz1LineText[32] = {0};
    sprintf(sz1LineText, "%s%s", DEV_ID_TIPS_GBK_STR, s_tLedScrParm.fnGetDevIdStr());
    led_set_area_display_content(0, (uint8_t *)sz1LineText, strlen(sz1LineText));
    // 设置第2行显示内容 "err：00"
    led_set_area_display_content(1, (uint8_t *)NETWORKING_GBK_STR, strlen(NETWORKING_GBK_STR));
    // 后续不显示内容
    led_ctrl_display_empty(2);
}

// ---------------------------------------------------------------------------------
// ---------------------------------------------------------------------------------
// ---------------------------------------------------------------------------------

/// @brief 设置LED屏幕的基本参数
/// @param hwHor 屏幕宽度（像素）
/// @param hwVer 屏幕高度（像素）
/// @param byFontSize 字体大小（像素）
/// @param byPixelPitch 像素间距（P?）
void led_scr_set_base_parm(uint16_t hwHor, uint16_t hwVer, uint8_t byFontSize, uint8_t byPixelPitch) {
    s_tLedScrParm.hwHor = hwHor;
    s_tLedScrParm.hwVer = hwVer;
    s_tLedScrParm.byFontSize = byFontSize;
    led_scr_set_bottom_parm(hwHor, hwVer, byFontSize, byPixelPitch);
};

void led_scr_set_time_fn(fn_v_pc fnGetDate, fn_v_pc fnGetTime, fn_u8_v fnGetWeekday) {
    s_tLedScrParm.fnGetDate = fnGetDate;
    s_tLedScrParm.fnGetTime = fnGetTime;
    s_tLedScrParm.fnGetWeekday = fnGetWeekday;
};

void led_scr_set_ope_fn1(fn_pc_v fnGetDevIdStr, fn_pc_v fnGetDevNameStr) {
    s_tLedScrParm.fnGetDevIdStr = fnGetDevIdStr;
    s_tLedScrParm.fnGetDevNameStr = fnGetDevNameStr;
};

void led_scr_set_ope_fn2(fn_u32_v fnGetNodeSum, fn_u32_u32 fnGetMntrSum) {
    s_tLedScrParm.fnGetNodeSum = fnGetNodeSum;
    s_tLedScrParm.fnGetMntrSum = fnGetMntrSum;
    _DEBUG_PRINTF(DBG_STP, "NodeSum=%u, Node[0].MntrSum=%u\n", s_tLedScrParm.fnGetNodeSum(), s_tLedScrParm.fnGetMntrSum(0));
};

void led_scr_set_ope_fn3(fn_pc_u32 fnGetNodeName, fn_pc_u32u32 fnGetMntrName, fn_pc_u32u32 fnGetMntrValue, fn_pc_u32u32 fnGetMntrUnit) {
    s_tLedScrParm.fnGetNodeName = fnGetNodeName;
    s_tLedScrParm.fnGetMntrName = fnGetMntrName;
    s_tLedScrParm.fnGetMntrValue = fnGetMntrValue;
    s_tLedScrParm.fnGetMntrUnit = fnGetMntrUnit;
};

/// @brief 设置每页显示时间（单位：秒）
/// @param uPerPageTime 每页显示时间（单位：秒）
void led_scr_set_per_page_time(uint32_t uPerPageTime) {
    s_tLedScrParm.uPerPageTime = uPerPageTime;
}

/// @brief LED控制状态机切换显示内容
/// @param byCtrlFlag 与 led_ctrl_flag_t 强绑定
void led_scr_switch_ctrl_flag(uint8_t byCtrlFlag) {
    s_tLedScrParm.byCtrlFlag = byCtrlFlag;
}

/// @brief LED控制状态机
void task_led_control_state_machine(void) {
    uint8_t  byFlag = 1;                                        // byFlag:是否与上次显示控制位一致
    uint8_t  byLastCtrlFlag = 0;
    uint32_t uQueueRet = 0;

    // 检查LED参数
    led_ctrl_check_parm();
    // 刷新LED屏幕参数
    refresh_led_scr_parm(); 

    // 创建队列
    asyn_led_ctrl_queue_parm_t tQueueData = {                   // 测试用
        .byLedCtrlFlag = LCF_show_data,
    };
    // asyn_led_ctrl_queue_parm_t tQueueData = {0};
    // tLedCtrlAsynQueue = xQueueCreate(TASK_HSA_LEN, TASK_HSA_SIZE);
    // if (tLedCtrlAsynQueue == NULL) {
    //     // 队列创建失败
    //     _DEBUG_PRINT(DBG_ERR, "queue err\n");
    //     assert_param(0);
    // }

    while (1) {
    //     uQueueRet = xQueueReceive(tLedCtrlAsynQueue, &tQueueData, s_tLedScrParm.uPerPageTime * 1000);
    //     if (uQueueRet == errQUEUE_EMPTY) {
    //         byFlag = 0;
    //     } else if (uQueueRet == pdPASS) {
    //         if (tQueueData.byLedCtrlFlag >= LCF_max) {
    //             _DEBUG_PRINT(DBG_ERR, "err\n");
    //             continue;
    //         }
    //         byFlag = tQueueData.byLedCtrlFlag == byLastCtrlFlag ? 1 : 0;            // 设置LED控制标志
    //         byLastCtrlFlag = tQueueData.byLedCtrlFlag;                              // 更新上次控制标志
    //     } else {
    //         _DEBUG_PRINT(DBG_ERR, "queue err\n");
    //         assert_param(0);
    //     }
        tLedCtrlTable[tQueueData.byLedCtrlFlag].fnLedCtrlFunc(byFlag);              // 调用LED控制函数
        STATE_MACHINE_DELAY_MS(1000);                                                // 释放CPU资源
    }
}
